
package com.hlkj.pay.app.merchant.ext.impl.hnapay;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import cn.hutool.core.date.DatePattern;
import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.extension.annotation.Extension;
import com.hlkj.framework.extension.executor.ExtensionExecutor;
import com.hlkj.framework.extension.scenario.BizScenario;
import com.hlkj.pay.app.merchant.ext.ChannelMerchantOrderExtPt;
import com.hlkj.pay.app.merchant.ext.MerchantExtConstant;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderQueryContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelRefundPayOrderContext;
import com.hlkj.pay.app.merchant.ext.impl.hnapay.constant.HnapayExtConstant;
import com.hlkj.pay.app.merchant.ext.impl.hnapay.dto.HnapayAppChannelParams;
import com.hlkj.pay.app.merchant.ext.impl.hnapay.dto.HnapayConfig;
import com.hlkj.pay.app.merchant.ext.impl.hnapay.utils.RecepitUtil;
import com.hlkj.pay.common.MerchantResultCode;
import com.hlkj.pay.dto.order.PayOrderChannelResultDto;
import com.hlkj.pay.dto.order.notify.CsOrderNotifyDto;
import com.hlkj.pay.dto.order.notify.RefundOrderNotifyDto;
import com.hlkj.pay.enums.CommonEnum;
import com.hlkj.pay.enums.PayEnums;
import com.hlkj.pay.enums.PayOrderEnums;
import com.hlkj.pay.infrastructure.model.order.PayOrderDO;
import com.hlkj.pay.infrastructure.model.order.RefundOrderDO;
import com.hlkj.pay.infrastructure.model.pay.PayWayDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.service.order.IPayOrderService;
import com.hlkj.pay.util.DateUtils;
import com.hlkj.pay.util.HttpRequestUtil;
import com.hlkj.pay.vo.order.req.PayOrderCommonParamReq;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.Map;

/**
 * @author HlpayTeam
 * @date 2024/10/15 18:16
 */
@Slf4j
@Repository
@RequiredArgsConstructor
@Extension(bizId = MerchantExtConstant.CHANNEL_HNA_PAY)
public class HnapayMerchantOrderExtPt implements ChannelMerchantOrderExtPt {

    private final ExtensionExecutor extensionExecutor;

    private final IPayOrderService payOrderService;

    @Value("${hlkj.pay.openapi.checkSign:true}")
    private boolean checkSign;

    @Override
    public CommonResult<String> channelOrderCode(ChannelPayOrderContext channelPayOrderContext) {
        return CommonResult.success();
    }

    @Override
    public void submitChannelPayOrder(ChannelPayOrderContext channelPayOrderContext) {
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        String payType = getPayType(payWayDO);
        if (!StringUtils.hasText(payType)) {
            channelPayOrderContext.setRemark("当前支付方式不支持");
            channelPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HnapayScenePayOrderExtPt.class, BizScenario.valueOf(payType), ex -> ex.submitChannelPayOrder(channelPayOrderContext));

    }

    @Override
    public void queryChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String payType = getPayType(payWayDO);
        if (!StringUtils.hasText(payType)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HnapayScenePayOrderExtPt.class, BizScenario.valueOf(payType), ex -> ex.queryChannelPayOrder(channelPayOrderQueryContext));

    }

    @Override
    public void closeChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String payType = getPayType(payWayDO);
        if (!StringUtils.hasText(payType)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HnapayScenePayOrderExtPt.class, BizScenario.valueOf(payType), ex -> ex.closeChannelPayOrder(channelPayOrderQueryContext));

    }

    @Override
    public void queryChannelClosePayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String payType = getPayType(payWayDO);
        if (!StringUtils.hasText(payType)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HnapayScenePayOrderExtPt.class, BizScenario.valueOf(payType), ex -> ex.queryChannelClosePayOrder(channelPayOrderQueryContext));

    }

    @Override
    public boolean refundApply(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String payType = getPayType(payWayDO);
        if (!StringUtils.hasText(payType)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return false;
        }
        return extensionExecutor.execute(HnapayScenePayOrderExtPt.class, BizScenario.valueOf(payType), ex -> ex.refundApply(channelRefundPayOrderContext));
    }

    @Override
    public void refundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String payType = getPayType(payWayDO);
        if (!StringUtils.hasText(payType)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HnapayScenePayOrderExtPt.class, BizScenario.valueOf(payType), ex -> ex.refundChannelPayOrder(channelRefundPayOrderContext));

    }

    @Override
    public void queryRefundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String payType = getPayType(payWayDO);
        if (!StringUtils.hasText(payType)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HnapayScenePayOrderExtPt.class, BizScenario.valueOf(payType), ex -> ex.queryRefundChannelPayOrder(channelRefundPayOrderContext));
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> orderStatusParse(HttpServletRequest servletRequest, ProviderChannelConfigDO providerChannelConfigDO) {
        Map<String, String> mapResult = HttpRequestUtil.requestFormParams(servletRequest);
//        try {
//            // 获取完整的 JSON 字符串
//            requestContext = params(servletRequest);
//        }
//        catch (Exception e) {
//            log.error("从当前线程中获取参数失败 从流中重新获取失败:{}", e.getMessage(),e);
//            return CommonResult.error(CommonResultCode.PARAMETER_MISSING);
//        }
        String requestContext = JsonUtils.toJsonString(mapResult);
        log.info("新生支付支付回调参数:{}", requestContext);
        CommonResult<PayOrderChannelResultDto> payOrderDOCommonResult;
        String orderNum = "";
        String signMsg = "";
        if (mapResult.containsKey("merOrderNum")) {
            orderNum = mapResult.get("merOrderNum");
            signMsg = mapResult.get("signMsg");
        } else if (mapResult.containsKey("merOrderId")) {
            orderNum = mapResult.get("merOrderId");
            signMsg = mapResult.get("signValue");
        } else {
            return CommonResult.error(MerchantResultCode.MERCHANT_PAY_ORDER_NOT_EXIST);
        }

        HnapayConfig payConfig = getPayConfig(providerChannelConfigDO.getParamsConfig(), orderNum);
        payOrderDOCommonResult = processCommonCall(mapResult);
        try {
            if (RecepitUtil.verify(mapResult.get("tranCode"), mapResult, payConfig.getHnaM2PublicKey(), signMsg)) {
                if (payOrderDOCommonResult.getData() != null)
                    payOrderDOCommonResult.getData().setCallBackBody(requestContext);
            } else {
                log.error("新生支付签名错误:{}", "验签失败");
                payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
                return payOrderDOCommonResult;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return payOrderDOCommonResult;
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> orderStatusParse(HttpServletRequest request) {
        return null;
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> merAuthParse(HttpServletRequest request) {
        return null;
    }

    @Override
    public PayOrderDO closeOrderStatusParse(HttpServletRequest callBackBody, ProviderChannelConfigDO providerChannelConfigDO) {
        return null;
    }

    @Override
    public CommonResult<CsOrderNotifyDto> csOrderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        return null;
    }

    @Override
    public CommonResult<RefundOrderNotifyDto> refundStatusParse(HttpServletRequest callBackBody, ProviderChannelConfigDO providerChannelConfigDO) {
        Map<String, String> mapResult = HttpRequestUtil.requestFormParams(callBackBody);
        String requestContext = JsonUtils.toJsonString(mapResult);
        log.info("新生退款订单参数:{}", JsonUtils.toJsonString(mapResult));
//        Map<String, String> mapResult = JsonUtils.parseObject(requestContext, Map.class);
        CommonResult<RefundOrderNotifyDto> payOrderDOCommonResult = CommonResult.success();
        try {
            HnapayConfig payConfig = getPayConfig(providerChannelConfigDO.getParamsConfig(), mapResult.get("orgMerOrderId"));
            if (RecepitUtil.verify(mapResult.get("tranCode"), mapResult, payConfig.getHnaM2PublicKey(), mapResult.get("signValue"))) {
                payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
            }
        } catch (Exception e) {
            log.error("新生退款回调异常", e);
            payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
        }

        RefundOrderNotifyDto refundOrderNotifyDto = new RefundOrderNotifyDto();
        refundOrderNotifyDto.setCallBackBody(requestContext);
        RefundOrderDO updateRefundOrderDO = new RefundOrderDO();
        refundOrderNotifyDto.setRefundOrder(updateRefundOrderDO);
        updateRefundOrderDO.setRefundOrderNo(mapResult.get("merOrderId"));
        if (mapResult.get("resultCode").equals("0000")) {
            switch (mapResult.get("orderStatus")) {
                case "1":
                    updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUND.getCode());
                    updateRefundOrderDO.setSuccessTime(DateUtils.getCurrentTime());
                    break;
                case "0":
                    updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUNDING.getCode());
                    break;
                default:
                    updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.FAIL.getCode());
            }
        }

        updateRefundOrderDO.setActualRefundAmount(new BigDecimal(mapResult.get("refundAmt")));
        payOrderDOCommonResult.setData(refundOrderNotifyDto);
        log.info("新生收到退款订单回调,解析后的订单数据为 payOrderDO:{}", JsonUtils.toJsonString(updateRefundOrderDO));
        return payOrderDOCommonResult;
    }

    @Override
    public String channelPayCode(PayWayDO payWayDO) {
        return getPayType(payWayDO);
    }

    @Override
    public Long queryChannelPayAppid(ChannelPayOrderContext channelPayOrderContext) {
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByType(payWayDO.getSubType());
        if (scenePayType == null) {
            return null;
        }
        CommonEnum.COMMON_APP_TYPE commonAppType = null;
        switch (PayEnums.PAY_TYPE.fromByCode(payWayDO.getType())) {
            case WECHAT:
                switch (scenePayType) {
                    case QR:
                    case JSAPI:
                        commonAppType = CommonEnum.COMMON_APP_TYPE.WECHAT_PUB;
                        break;
                    // case MINI_APP:
                    case H5:
                    case APP:
                        commonAppType = CommonEnum.COMMON_APP_TYPE.WECHAT_MIN;
                        break;
                }
                break;
            case ALIPAY:
                switch (scenePayType) {
                    case JSAPI:
                        commonAppType = CommonEnum.COMMON_APP_TYPE.ALIPAY_PUB;
                        break;
                    case MINI_APP:
                        commonAppType = CommonEnum.COMMON_APP_TYPE.ALIPAY_MIN;
                        break;
                }
                break;
        }
        if (commonAppType == null) {
            return null;
        }

        ProviderChannelConfigDO providerChannelConfigDO = channelPayOrderContext.getProviderChannelConfigDO();
        switch (commonAppType) {
            case WECHAT_PUB:
                return providerChannelConfigDO.getWechatDefaultPubId();
            case WECHAT_MIN:
                return providerChannelConfigDO.getWechatDefaultMiniId();
            case ALIPAY_PUB:
                return providerChannelConfigDO.getAlipayPubId();
            case ALIPAY_MIN:
                return providerChannelConfigDO.getAlipayMiniId();
        }
        return null;
    }

    @Override
    public String callBackResponse(CommonResult commonResult) {
        String rspStr = "RespCode=200";
        if (!commonResult.isSuccess()) {
            rspStr = "FAIL";
        }
        return rspStr;
    }

    @Override
    public boolean wechatLink(ChannelPayOrderContext channelPayOrderContext) {
        PayOrderCommonParamReq.Extra extra = channelPayOrderContext.getExtra();
        if (extra != null && !CommonEnum.YES_NO_TYPE.NO.getCode().equals(extra.getOriginalType())) {
            return false;
        }
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByType(payWayDO.getSubType());
        PayEnums.PAY_TYPE payType = PayEnums.PAY_TYPE.fromByCode(payWayDO.getType());
        // 只有微信 app h5才需要
        switch (payType) {
            case WECHAT:
                break;
            default:
                return false;
        }
        switch (scenePayType) {
            case APP:
            case H5:
                break;
            default:
                return false;
        }
        return true;
    }

    /**
     * 汇付支付方式路由
     *
     * @param payWayDO
     * @return
     */
    private String getPayType(PayWayDO payWayDO) {
        log.info("新生支付实际支付type:{}", payWayDO.getSubType());
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByType(payWayDO.getSubType());
        if (scenePayType == null) {
            return null;
        }
        PayEnums.PAY_TYPE payType = PayEnums.PAY_TYPE.fromByCode(payWayDO.getType());

        switch (payType) {
            case WECHAT:
                switch (scenePayType) {
                    case SCAN:
                        return HnapayExtConstant.SCAN_PAY;
                    case H5:
                    case APP:
                    case MINI_APP:
                        return HnapayExtConstant.JSAPI;
                }
                return HnapayExtConstant.NATIVE_PAY;
            case ALIPAY:
                switch (scenePayType) {
                    case SCAN:
                        return HnapayExtConstant.SCAN_PAY;
                    default:
                        return HnapayExtConstant.NATIVE_PAY;
                }
        }
        return null;
    }

    /**
     * 通用回调参数解析
     *
     * @param tradeRequest
     * @return
     */
    CommonResult<PayOrderChannelResultDto> processCommonCall(Map<String, String> tradeRequest) {
        log.info("新生支付支付回调对象参数:{}", tradeRequest);

        PayOrderChannelResultDto payOrderChannelResultDto = new PayOrderChannelResultDto();
        PayOrderDO updartePayOrderDO = new PayOrderDO();
        payOrderChannelResultDto.setUpdatePayOrderDO(updartePayOrderDO);
        CommonResult<PayOrderChannelResultDto> commonResult = CommonResult.success(payOrderChannelResultDto);
        String resCode = "";
        if ("WS01".equals(tradeRequest.get("tranCode"))) {
            updartePayOrderDO.setPayOrderNo(tradeRequest.get("merOrderNum"));
            resCode = tradeRequest.get("respCode");
        } else {
            updartePayOrderDO.setPayOrderNo(tradeRequest.get("merOrderId"));
            resCode = tradeRequest.get("resultCode");
        }

        //20250623182156
        if (resCode.equals("0000")) {
            updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.SUCCESS.getCode());
            updartePayOrderDO.setSuccessTime(DateUtils.shanghaiDate(tradeRequest.get("tranFinishTime"), DatePattern.PURE_DATETIME_PATTERN));
        }

        if (StringUtils.hasText(tradeRequest.get("userId"))) {
            updartePayOrderDO.setUserId(tradeRequest.get("userId"));
        }

        if (StringUtils.hasText(tradeRequest.get("bankOrderId"))) {
            updartePayOrderDO.setChannelOrderNo(tradeRequest.get("bankOrderId"));
        }
        updartePayOrderDO.setPayAmount(new BigDecimal(tradeRequest.get("tranAmt")));
        updartePayOrderDO.setInsOrderNo(tradeRequest.get("hnapayOrderId"));
        return commonResult;
    }

    String params(HttpServletRequest request) throws Exception {
        // 读取请求体中的内容
        StringBuilder stringBuilder = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                stringBuilder.append(line);
            }
        }
        log.warn("新生支付回调返回参数:{}", stringBuilder);
        // 获取完整的 JSON 字符串
        return stringBuilder.toString();
    }

    //转换是如果商户有单独配置 取商户配置
    public HnapayConfig getPayConfig(String channelConfig, String payOrderNo) {
        // 配置转换
        HnapayConfig payConfig = JsonUtils.parseObject(channelConfig, HnapayConfig.class);

        PayOrderDO orderDO = payOrderService.queryPayOrderNoCache(payOrderNo);
        HnapayAppChannelParams appChannelParams = JsonUtils.parseObject(orderDO.getChannelConfig(), HnapayAppChannelParams.class);

        if (StringUtils.hasText(appChannelParams.getMerId()))
            payConfig.setMerId(appChannelParams.getMerId());

        if (StringUtils.hasText(appChannelParams.getHnaM2PublicKey()))
            payConfig.setHnaM2PublicKey(appChannelParams.getHnaM2PublicKey());

        if (StringUtils.hasText(appChannelParams.getMchM2PrivateKey()))
            payConfig.setMchM2PrivateKey(appChannelParams.getMchM2PrivateKey());

        return payConfig;
    }
}
